﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Messaging;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Xml.Serialization;
using Apache.NMS;
using Apache.NMS.ActiveMQ;
using Apache.NMS.Util;
using BusinessLogic.Dto;
using Common;

namespace BusinessLogic
{
    public class Facade
    {
        private static Facade INSTANCE;
        private static object LOCK = new object();

        public static Facade Instance
        {
            get
            {
                lock (LOCK)
                {
                    if (INSTANCE == null)
                        INSTANCE = new Facade();
                }

                return INSTANCE;
            }
        }

        private Facade()
        {
        }

        public int GetNextOrderNumber()
        {
            int res = -1;
            using (var context = new WGRUSEntities())
            {
                res = context.Database.SqlQuery<int>("SELECT NEXT VALUE FOR OrderNumber").FirstOrDefault();
            }
            return res;
        }

        public StandingReport IsStandingValid(XDocument orderDocument)
        {
            bool validStanding = false;

            StandingReport res = new StandingReport();

            //Opret order i databasen
            //To Class
            XmlSerializer xmlSerializer = new XmlSerializer(typeof (OrderType));
            OrderType ot = (OrderType) xmlSerializer.Deserialize(orderDocument.CreateReader());

            CustomerOrderType c = ot.Customer;
            OrderDetailsType od = ot.OrderDetails;
            OrderLineType[] lines = ot.OrderDetails.Orderlines;

            res.CustomerId = c.CustomerId;

            using (var context = new WGRUSEntities())
            {
                Customer customer = context.Customers.FirstOrDefault(cust => cust.TelefonNr == c.CustomerId);
                if (customer == null)
                {
                    res.BadStandingReason = "Invalid customer";
                    res.IsStandingValid = false;
                    return res;
                }

                if (!customer.Approved)
                {
                    res.BadStandingReason = "Customer not approved";
                    res.IsStandingValid = false;
                    return res;
                }

                decimal customerOpenOrderTotal = customer.Orders.
                    Where(
                        oo => oo.OrderStateId >= (int) OrderStateEnum.Shipped &&
                              oo.OrderStateId < (int) OrderStateEnum.PaymentRecieved).
                    Sum(oo => oo.Total);

                Order order = new Order
                {
                    KundeNr = c.CustomerId,
                    LeveringAtt = od.LeveringAtt,
                    LeveringBy = od.LeveringBy,
                    LeveringGade = od.LeveringGade,
                    LeveringNavn = od.LeveringNavn,
                    LeveringPostNr = od.LeveringPostNr,
                    Oprettet = DateTime.Now,
                    OrdreId = od.OrdreId,
                    Total = od.Total,
                    OrderStateId = (int) OrderStateEnum.StandingValidationFailed
                };

                customer.Orders.Add(order);

                foreach (var l in lines)
                {
                    OrderLine ol = new OrderLine
                    {
                        VareId = l.VareId,
                        VareBeskrivelse = l.VareBeskrivelse,
                        Antal = l.Antal,
                        Enhedspris = l.Enhedspris,
                    };
                    order.OrderLines.Add(ol);
                }


                //Beregn Standing status
                decimal total = order.OrderLines.Sum(ol => (decimal) ol.Antal*ol.Enhedspris);
                order.Total = total;
                decimal remaining = customer.CreditLimit - customerOpenOrderTotal - total;

                validStanding = remaining >= 0;

                //Update Standing status i databasen
                if (validStanding)
                {
                    order.OrderStateId = (int) OrderStateEnum.StandingValidated;
                    res.IsStandingValid = true;
                }
                else
                {
                    order.OrderStateId = (int) OrderStateEnum.StandingValidationFailed;
                    res.IsStandingValid = false;
                    res.BadStandingReason = "Credit limit exceeded";
                }

                context.SaveChanges();
            }

            //returner true ved "good standing" og false ved "bad standing"
            return res;

        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public CustomerUpdateReport UpdateCustomer(XDocument customerDocument)
        {
            CustomerUpdateReport res = new CustomerUpdateReport {IsSuccesfull = true};
            //To Class
            XmlSerializer xmlSerializer = new XmlSerializer(typeof (CustomerType));
            CustomerType ct = (CustomerType) xmlSerializer.Deserialize(customerDocument.CreateReader());
            //Tjek om kunden eksisterer

            using (var context = new WGRUSEntities())
            {
                Customer c = context.Customers.FirstOrDefault(cust => cust.TelefonNr == ct.CustomerId);
                //Ja - opdater kunden (Adresse data)
                if (c != null)
                {
                    c.Navn = ct.Navn;
                    c.Email = ct.Email;
                    c.Gade = ct.Gade;
                    c.PostNr = ct.PostNr;
                    c.By = ct.By;
                }
                    //Nej - opret kunden - credit = 0, approved = false
                else
                {
                    c = new Customer
                    {
                        TelefonNr = ct.CustomerId,
                        Navn = ct.Navn,
                        Email = ct.Email,
                        Gade = ct.Gade,
                        PostNr = ct.PostNr,
                        By = ct.By,
                        Approved = false,
                        CreditLimit = 0,
                        Oprettet = ct.Oprettet
                    };

                    context.Customers.Add(c);
                }

                context.SaveChanges();
            }
            return res;
        }

        public ShipOrderReport OrderShipped(XDocument doc)
        {
            ShipOrderReport res = new ShipOrderReport {IsSuccesfull = true};
            //To Class
            XmlSerializer xmlSerializer = new XmlSerializer(typeof (OrderType));
            OrderType ot = (OrderType) xmlSerializer.Deserialize(doc.CreateReader());
            //Opdater ordre status
            using (var context = new WGRUSEntities())
            {
                Order o = context.Orders.FirstOrDefault(oo => oo.OrdreId == ot.OrderDetails.OrdreId);
                if (o != null)
                {
                    o.OrderStateId = (int) OrderStateEnum.Shipped;
                    context.SaveChanges();
                }
                else
                {
                    res.IsSuccesfull = false;
                    res.ErrorReason = "Invalid OrderId";
                    res.OrderId = ot.OrderDetails.OrdreId;
                }
            }

            return res;
        }

        public void SendInvoice(int orderId)
        {
            //Opdater ordre status

            using (var context = new WGRUSEntities())
            {
                Order o = context.Orders.FirstOrDefault(oo => oo.OrdreId == orderId);
                if (o != null)
                {
                    o.OrderStateId = (int) OrderStateEnum.Invoiced;
                    context.SaveChanges();
                }
            }
        }

        /// <summary>
        /// En kunder er blevet opdateret. Denne information skal rundsendes til øvrige systemer
        /// </summary>
        /// <param name="e"></param>
        internal void NotifyCustomerChanged(CustomerChangedEventArgs e)
        {
            CustomerType ct = ModelToDto.FillCustomerType(e.Customer);

            var sw = new StringWriter();
            var xmlSer = new XmlSerializer(typeof (CustomerType));
            var noNamespaces = new XmlSerializerNamespaces();
            noNamespaces.Add("", "");
            xmlSer.Serialize(sw, ct, noNamespaces);

            ConnectionFactory factory = new ConnectionFactory(Constants.QUEUE_URI);
            using (IConnection connection = factory.CreateConnection())
            using (ISession session = connection.CreateSession())
            {
                IDestination destination = SessionUtil.GetDestination(session, Constants.ACCOUNTING_CUSTOMER_UPDATE_QUEUE);
                using (IMessageProducer producer = session.CreateProducer(destination))
                {
                    producer.DeliveryMode = MsgDeliveryMode.Persistent;
                    ITextMessage request = session.CreateTextMessage(sw.ToString());
                    //request.NMSCorrelationID = "abc";
                    //request.Properties["NMSXGroupID"] = "cheese";
                    //request.Properties["myHeader"] = "Cheddar";
                    producer.Send(request);
                }
            }
        }
    }
}